package cc.eddic.pratice.programmingparadigms.streampractice.impl;


import cc.eddic.pratice.programmingparadigms.emp02list.impl.ResultListGcsImpl;
import cc.eddic.pratice.programmingparadigms.exam.QuestionType;
import cc.eddic.pratice.programmingparadigms.exam.domain.*;

import cc.eddic.pratice.programmingparadigms.exam.impl.QuestionBankServiceLhImpl;

import cc.eddic.pratice.programmingparadigms.streampractice.QuestionAccuracy;
import cc.eddic.pratice.programmingparadigms.streampractice.QuestionAndAnswer;
import cc.eddic.pratice.programmingparadigms.streampractice.ScoreOfStudentAndCategory;
import cc.eddic.pratice.programmingparadigms.streampractice.StreamPractice;
import lombok.Getter;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

public class StreamPracticeLhImpl implements StreamPractice {
    private static final Logger log = LoggerFactory.getLogger(ResultListGcsImpl.class);

    @Override
    public List<QuestionAndAnswer> mapToQuestionAndAnswer(List<Question> questionList) {
        return questionList.stream().map(
                qu -> new QuestionAndAnswer(
                        qu.getNo(),
                        qu.getDescription(),
                        qu.getOptions().stream().filter(Option::isCorrectAnswer).findFirst()
                                .orElse(new Option(" ", ' ', true)).getChoice(),
                        qu.getOptions().stream().filter(Option::isCorrectAnswer).findFirst()
                                .orElse(new Option(" ", ' ', true)).getAnswer()
                )).collect(Collectors.toList());
    }

    public List<QuestionAndAnswer> forToQuestionAndAnswer(List<Question> questionList) {
        List<QuestionAndAnswer> questionAndAnswerList = new ArrayList<>();
        for (Question question : questionList) {
            char choice = ' ';
            String answer = "无正确答案";
            for (int optionindex = 0; optionindex < question.getOptions().size(); optionindex++) {
                boolean iscorrect = question.getOptions().get(optionindex).isCorrectAnswer();
                if (iscorrect) {
                    choice = question.getOptions().get(optionindex).getChoice();
                    answer = question.getOptions().get(optionindex).getAnswer();
                    break;
                }
            }
            questionAndAnswerList.add(new QuestionAndAnswer(
                    question.getNo(),
                    question.getDescription(),
                    choice, answer
            ));
        }
        return questionAndAnswerList;
    }


    public List<QuestionAccuracy> calculateAccuracy(List<Answer> answerList) {
        Option defaultOption = new Option(" ", ' ', true);
        List<categoryScoreModle> categoryModleList = answerList.stream().map(
                answer -> new categoryScoreModle(
                        answer.getQuestion().getNo(),
                        answer.getQuestion().getDescription(),
                        answer.getStudent().getName(),
                        answer.getChoice(),
                        answer.getQuestion().getOptions().stream()
                                .filter(Option::isCorrectAnswer)
                                .findFirst().orElse(defaultOption).getChoice()
                )
        ).collect(Collectors.toList());
        long num = categoryModleList.stream().map(categoryScoreModle::getStudentname).distinct().count();

        List<Question> questionList = answerList.stream().map(Answer::getQuestion).collect(Collectors.toList());

        Question defaultQuestion = new Question(-1, " ", QuestionType.SINGLE_CHOICE);
        return categoryModleList.stream().
                filter(categoryScoreModle::iscorrect)
                .collect(Collectors.groupingBy(categoryScoreModle::getQuestionno, Collectors.counting()))
                .entrySet().stream()
                .map(categoryModle -> new QuestionAccuracy(categoryModle.getKey(),
                        questionList.stream().filter(question -> question.getNo() == categoryModle.getKey()).findFirst()
                                .orElse(defaultQuestion).getDescription(),
                        categoryModle.getValue() * 1d / num)
                ).sorted((Comparator.comparingDouble(QuestionAccuracy::getAccuracy).thenComparingInt(QuestionAccuracy::getNo)).reversed())
                .collect(Collectors.toList());
    }

    @Getter
    private static class categoryScoreModle {
        int questionno;
        String questiondescription;
        String studentname;
        char choice;
        char correctchoice;

        public categoryScoreModle(int questionno, String questiondescription, String studentname, char choice, char correctchoice) {
            this.questionno = questionno;
            this.questiondescription = questiondescription;
            this.studentname = studentname;
            this.choice = choice;
            this.correctchoice = correctchoice;
        }

        public boolean iscorrect() {
            return choice == correctchoice;
        }
    }

    QuestionBankServiceLhImpl qbs = new QuestionBankServiceLhImpl();

    public List<QuestionAccuracy> calculateAccuracy1(List<Answer> answerList) {
        Map<String, Integer> studentNum = new HashMap<>();
        Map<Integer, Integer> rightNum = new HashMap<>();
        for (Answer answer : answerList) {
            studentNum.put(answer.getStudent().getName(), 1);
            boolean iscorrect = answer.getQuestion().getOptions().stream().
                    anyMatch(option -> option.isCorrectAnswer() && option.getChoice() == answer.getChoice());
            rightNum.merge(answer.getQuestion().getNo(), iscorrect ? 1 : 0, Integer::sum);
        }
        int num = studentNum.keySet().size();
        return rightNum.entrySet().stream().map(q -> new QuestionAccuracy(
                q.getKey(),
                qbs.importQuestions(" ").stream().filter(question -> question.getNo() == q.getKey()).findFirst()
                        .orElseThrow(NullPointerException::new).getDescription()
                , q.getValue() * 100D / num
        )).collect(Collectors.toList());
    }


    /*
    尝试只使用stream,新建categoryScore分类，未实现，有错误值
     */
    public List<QuestionAccuracy> calculateAccuracy2(List<Answer> answerList) {
        return answerList.stream().
                map(answer -> new categoryModle1(
                        answer.getQuestion().getDescription(),
                        answer.getChoice(),
                        answer.getQuestion().getOptions().stream()
                                .filter(option -> option.isCorrectAnswer() && option.getChoice() == answer.getChoice())
                                .findFirst().orElse(new Option(" ", ' ', true)).getChoice(),
                        answer.getQuestion().getNo()
                )).filter(categoryModle -> categoryModle.choice == categoryModle.correctAnswer)
                .collect(Collectors.groupingBy(categoryModle1::getQuestionno, Collectors.counting()))
                .entrySet().stream()
                .map(qa -> new QuestionAccuracy(
                        qa.getKey(),
                        answerList.stream().filter(question -> question.getQuestion().getNo() == qa.getKey()).findFirst()
                                .orElseThrow(NullPointerException::new).getQuestion().getDescription(),
                        qa.getValue() * 1d / 9))
                .collect(Collectors.toList());
    }

    @Getter
    static class categoryModle1 {
        String question;
        char choice;
        char correctAnswer;
        int questionno;

        public categoryModle1(String question, char choice, char correctAnswer, int questionno) {
            this.question = question;
            this.choice = choice;
            this.correctAnswer = correctAnswer;
            this.questionno = questionno;
        }
    }

    public List<Question> importCategories(String categoryExcelFile) {
        try {
            HashMap<String, String> categoryMap = new HashMap<>();
            categoryMap.put("基础", "软件开发基础");
            categoryMap.put("HTML", "软件开发基础");
            categoryMap.put("SQL", "软件开发基础");
            categoryMap.put("DOS", "软件开发基础");
            categoryMap.put("Linux", "软件开发基础");
            categoryMap.put("数学", "软件开发基础");

            categoryMap.put("Word", "办公软件");
            categoryMap.put("Excel", "办公软件");

            categoryMap.put("代码", "代码");


            FileInputStream file = new FileInputStream(categoryExcelFile);
            Workbook workbook = new XSSFWorkbook(file);

            List<Question> questionList = qbs.importCorrectAnswers(" ");

            Sheet categorySheet = workbook.getSheetAt(0);
            Iterator<Row> rowIterator = categorySheet.rowIterator();

            rowIterator.next();
            Row rowSubCategory = rowIterator.next();

            while (rowIterator.hasNext()) {
                try {
                    Row row = rowIterator.next();
                    CellType cellType = row.getCell(0).getCellType();
                    if (cellType == CellType.STRING) {
                        break;
                    }
                    int no = (int) row.getCell(0).getNumericCellValue();


                    Iterator<Cell> cellIterator = row.cellIterator();
                    while (cellIterator.hasNext()) {
                        Cell cell = cellIterator.next();
                        if (cell.getNumericCellValue() == 1) {
                            int columnIndex = cell.getColumnIndex();
                            String nameSubCategory = rowSubCategory.getCell(columnIndex).getRichStringCellValue().getString();
                            SubCategory subcategory = new SubCategory(nameSubCategory);
                            subcategory.setCategory(new Category(categoryMap.get(nameSubCategory)));
                            questionList.get(no - 1).setSubCategory(subcategory);
                            break;
                        }
                    }

                } catch (NumberFormatException e) {
                    break;
                }
            }

            return questionList;
        } catch (FileNotFoundException e) {
            log.error("importCategories FileInputStream FileNotFoundException\n");
        } catch (IOException e) {
            log.error("importCategories XSSFWorkbook IOException\n");
        }
        return null;
    }

    @Getter
    private static class categoryScore {
        String studentname;
        String category;
        char choice;
        char correctchoice;

        public categoryScore(String studentname, String category, char choice, char correctchoice) {
            this.studentname = studentname;
            this.category = category;
            this.choice = choice;
            this.correctchoice = correctchoice;
        }
    }

    //计算各分类得分，categoriesQuestion中含有正确答案和分类
    public List<ScoreOfStudentAndCategory> calculateScoreOfStudentAndSubCategory(List<Answer> answerList) {
        Option defaultOption = new Option(" ", ' ', true);

        List<categoryScore> categoryModleList = answerList.stream().map(
                answer -> new categoryScore(answer.getStudent().getName(),
                        answer.getQuestion().getSubCategory().getName(),
                        answer.getChoice(),
                        answer.getQuestion().getOptions().stream().filter(Option::isCorrectAnswer).findFirst()
                                .orElse(defaultOption).getChoice())
        ).collect(Collectors.toList());
        List<categoryScore> categoryModleList1 = categoryModleList.stream().filter(ca -> ca.choice == ca.correctchoice).collect(Collectors.toList());


        return categoryModleList1.stream()
                .collect(Collectors.groupingBy(categoryScore::getStudentname,
                        Collectors.groupingBy(categoryScore::getCategory,Collectors.counting())))
                .entrySet().stream()
                .flatMap(stu->stu.getValue().entrySet().stream()
                        .map(category->new ScoreOfStudentAndCategory(
                        stu.getKey(),category.getKey(),category.getValue().intValue()*4)))
                .collect(Collectors.toList());
    }
}

